#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QTimer>
#include <QFileDialog>
#include "vehicle.h"
#include "driver.h"
#include "streetmap.h"
#include "utilities.h"

MainWindow::MainWindow(QWidget *parent) :
  QMainWindow(parent),
  ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  mWorld = World::instance();
  mSelectedVehicle = NULL;
  mIsPlaying = false;

  if (mWorld)
  {
    mRealtimeSim = ui->actionRun_in_Real_Time->isChecked();

    // Start GUI timer
    mTimer = new QTimer(this);
    connect(mTimer, SIGNAL(timeout()), this, SLOT(OnTimer()));
    mTimer->setInterval(50);
    if (mRealtimeSim)
      mTimer->start();

    // Set the first option pane to view the scenario settings
    ui->optionsStackedWidget->setCurrentIndex(0);

    // Default on application start
    mWorld->loadScenarioXML("config/test_scenario.xml");
    mWorld->loadTrafficControlXML("config/test_controls.xml");
    ui->graphicsView->rebuildMapGraphics();
    mWorld->mFrameManager.addFrame();
    ui->graphicsView->refreshGraphics();

    ui->graphicsView->centerOnContent();
    ui->graphicsView->showForces(ui->actionForces->isChecked());
    ui->graphicsView->showVectors(ui->actionVectors->isChecked());
    ui->graphicsView->setSelectionOnly(ui->actionSelection_Only->isChecked());

    OnSimSpeedSlider(ui->SimSpeedSlider->value());
    connect(ui->graphicsView->scene(), SIGNAL(selectionChanged()), this, SLOT(OnSelectionChanged()));

    // Start pre-calculating scenario
    if (!mRealtimeSim)
      mWorld->start();
  }
  else
    qDebug("MAINWINDOW: ERROR - World not created!");
}

MainWindow::~MainWindow()
{
  mSelectedVehicle = NULL;

  delete ui;

  if (mWorld)
  {
    mWorld->setExiting(true);
    mWorld->clearScenario();
    if (mWorld->wait())
      mWorld->destroyInstance();
  }
}

void MainWindow::OnSpawnVehicle()
{
  if (mWorld)
  {
    QString typeName = mWorld->mVehicleTypes.first()->name;
    Vehicle *v = mWorld->addVehicle("NewVehicle", typeName);
    typeName = mWorld->mDriverTypes.first()->name;
    Driver *d = mWorld->addDriver("NewDriver", typeName);
    d->getIn(v);
    v->setPosition(Point(100.0, 100.0));
  }
}

void MainWindow::OnZoomIn()
{
  ui->graphicsView->zoom(1);
}

void MainWindow::OnZoomOut()
{
  ui->graphicsView->zoom(0);
}

void MainWindow::OnSimSpeedSlider(int speed)
{
  if (!mWorld)
    mWorld = World::instance();

  mTimer->stop();
  switch (speed)
  {
  case 5: mTimer->setInterval(10); break;   // 100hz
  case 4: mTimer->setInterval(20); break;   // 50hz
  case 3: mTimer->setInterval(50); break;   // 20hz
  case 2: mTimer->setInterval(100); break;  // 10hz
  default:
  case 1: mTimer->setInterval(1000); break; // 1hz
  }
  mTimer->start();
}

void MainWindow::updateUI()
{

  ui->SimTime->setText(mWorld->mFrameManager.currentFrame()->mTime.toString("hh:mm:ss.zzz"));

  if (mSelectedVehicle && mRealtimeSim)
  {
    if (ui->actionTrack_Selection->isChecked())
      ui->graphicsView->centerOn(mSelectedVehicle->position());
    ui->VNameBox->setText(mSelectedVehicle->name());
    ui->AclBox->setText(mSelectedVehicle->acceleration().toString(2));
    ui->VelBox->setText(mSelectedVehicle->velocity().toString(2));
    ui->PosBox->setText(mSelectedVehicle->position().toString(2));
    QString tmp = QString::number(mSelectedVehicle->speed(), 'f', 2);
    tmp += " (" + QString::number(mSelectedVehicle->speed(true), 'f', 2) + "mph)";
    ui->SpeedBox->setText(tmp);
    tmp = mSelectedVehicle->direction().toString(2);
    tmp += " (" + QString::number(mSelectedVehicle->direction().angle(), 'f', 1) + "deg)";
    ui->HdgBox->setText(tmp);
    qreal acl, brk, str;
    mSelectedVehicle->actuators(acl, brk, str);
    tmp = QString::number(acl, 'f', 3) + "/" + QString::number(brk, 'f', 3) + "/" + QString::number(str, 'f', 3);
    ui->ABSBox->setText(tmp);
    ui->AccelBar->setValue(acl*100.0f);
    ui->BrakeBar->setValue(brk*100.0f);
    ui->SteerWheel->setValue(str*90.0f);

    Driver *d = mSelectedVehicle->driver();
    if (d != NULL)
    {
      ui->DNameBox->setText(d->name());
      tmp = QString::number(d->mDesiredSpeed * MPH_TO_FTPS, 'f', 2);
      tmp += " (" + QString::number(d->mDesiredSpeed, 'f', 2) + "mph)";
      tmp += " / d=" + QString::number(d->mSpeedDelta * MPH_TO_FTPS, 'f', 2);
      tmp += " (" + QString::number(d->mSpeedDelta, 'f', 2) + "mph)";
      ui->DesSpd->setText(tmp);
      tmp = QString::number(d->mDesiredDirection, 'f', 1) + "deg";
      tmp += " / d=" + QString::number(d->mHeadingDelta, 'f', 1) + "deg";
      ui->DesHdg->setText(tmp);
      tmp = d->mCurrentGoal.toString(2);
      tmp += " (" + QString::number(d->mDistToGoal, 'f', 2) + "ft)";
      tmp += " (" + QString::number(d->mTimeToGoal, 'f', 2) + "sec)";
      ui->Goal->setText(tmp);
      tmp = d->mCurrentWaypoint.toString(2);
      ui->Waypoint->setText(tmp);
      if (d->mYieldingTo)
        ui->Yield->setText(d->mYieldingTo->name());
      else
        ui->Yield->clear();
    }
  }
  else
  {
    ui->DNameBox->clear();
    ui->DesHdg->clear();
    ui->DesSpd->clear();
    ui->Goal->clear();
    ui->ABSBox->clear();
    ui->HdgBox->clear();
    ui->SpeedBox->clear();
    ui->PosBox->clear();
    ui->AclBox->clear();
    ui->VelBox->clear();
    ui->VNameBox->clear();
    ui->AccelBar->setValue(0.0f);
    ui->BrakeBar->setValue(0.0f);
    ui->SteerWheel->setValue(0.0f);
    ui->Waypoint->clear();
  }
}

void MainWindow::OnTimer()
{
  if (!mWorld)
    mWorld = World::instance();

  if (!mIsPlaying)
  {
    ui->SimPlayStopButton->setIcon(QIcon(":/gfx/playbutton.png"));
    return;
  }

  ui->SimPlayStopButton->setIcon(QIcon(":/gfx/stopbutton.png"));

  // Advance world with GUI timer
  if (mRealtimeSim)
    mWorld->tick();

  mWorld->mFrameManager.advanceFrame();
  ui->graphicsView->refreshGraphics();

  updateUI();

}

void MainWindow::OnClearScenario()
{
  if (mWorld)
  {
    qDebug() << "MAINWINDOW: Clear Scenario";
    ui->graphicsView->clearGraphics(true);
    mWorld->clearScenario();
    mWorld->mFrameManager.clearFrames();
  }
}

void MainWindow::OnLoadScenario()
{
  QString fileName = QFileDialog::getOpenFileName(this,
      tr("Open Scenario"), "./config", tr("Scenario Files (*.xml)"));
  if (!fileName.isEmpty())
  {
    mWorld = World::instance();
    qDebug() << "MAINWINDOW: Load Scenario> " << fileName;
    mWorld->clearMap(); // don't necessarily want to clear map here
    mWorld->clearScenario();
    ui->graphicsView->clearGraphics(true);
    mWorld->mFrameManager.clearFrames();

    mWorld->loadScenarioXML(fileName);
    ui->graphicsView->rebuildMapGraphics();
    mWorld->mFrameManager.addFrame();
    ui->graphicsView->refreshGraphics();

    ui->graphicsView->centerOnContent();
    // Start pre-calculating scenario
    if (!mRealtimeSim)
      mWorld->start();
  }
}

void MainWindow::OnLoadTrafficControls()
{
  QString fileName = QFileDialog::getOpenFileName(this,
      tr("Open Controls"), "./config", tr("Control Files (*.xml)"));
  if (!fileName.isEmpty())
  {
    if (!mWorld)
      mWorld = World::instance();

    ui->graphicsView->clearGraphics();
    mWorld->mFrameManager.clearFrames();

    mWorld->reloadScenario();
    mWorld->loadTrafficControlXML(fileName);
    mWorld->mFrameManager.addFrame();
    ui->graphicsView->refreshGraphics();
    qDebug() << "MAINWINDOW: Load Controls> " << fileName;
    //ui->graphicsView->rebuildMapGraphics();
  }
}

void MainWindow::OnMenuAction(QAction *action)
{
  //qDebug() << "MAINWINDOW: OnMenuAction>" << action->text();
  if (action->text() == "Exit")
    QApplication::exit();
}

void MainWindow::OnTracking(bool value)
{
}

void MainWindow::OnSelectionChanged()
{
  QGraphicsScene *scene = ui->graphicsView->scene();
  if (scene && !scene->selectedItems().isEmpty())
  {
    QGraphicsItem *g = scene->selectedItems().first();
    if (g)
    {
      QString name = g->toolTip();
      Vehicle *v = mWorld->findVehicle(name);
      if (v)
        mSelectedVehicle = v;
    }
  }
  else
  {
    mSelectedVehicle = NULL;
  }
  ui->graphicsView->setSelectedVehicle(mSelectedVehicle);
  updateUI();
}

void MainWindow::OnShowVectors(bool value)
{
  ui->graphicsView->showVectors(value);
  ui->graphicsView->hide();
  ui->graphicsView->show();
}

void MainWindow::OnShowForces(bool value)
{
  ui->graphicsView->showForces(value);
  ui->graphicsView->hide();
  ui->graphicsView->show();
}

void MainWindow::OnSelectionOnly(bool value)
{
  ui->graphicsView->setSelectionOnly(value);
  ui->graphicsView->hide();
  ui->graphicsView->show();
}

void MainWindow::OnRunInRealTime(bool value)
{
  mIsPlaying = false;
  mRealtimeSim = value;

  ui->graphicsView->clearGraphics();
  mWorld->mFrameManager.clearFrames();
  mWorld->reloadScenario();

  mWorld->mFrameManager.addFrame();
  ui->graphicsView->refreshGraphics();

  if (!mRealtimeSim)
    mWorld->start();
}

void MainWindow::OnSimBegin()
{
  mIsPlaying = false;
//qDebug() << "MAINWINDOW: OnSimBegin>";
  if (mRealtimeSim)
  {
    QString selName;
    if (mSelectedVehicle)
      selName = mSelectedVehicle->name();

    if (mWorld)
    {
      ui->graphicsView->clearGraphics();
      mWorld->mFrameManager.clearFrames();
      mWorld->reloadScenario();

      mWorld->mFrameManager.addFrame();
      ui->graphicsView->refreshGraphics();

      if (!selName.isEmpty())
      {
        mSelectedVehicle = mWorld->findVehicle(selName);
        ui->graphicsView->setSelectedVehicle(mSelectedVehicle);
      }
    }
  }
  else // already calculated, just move to frame 0
  {
    mWorld->mFrameManager.setCurrentFrameNumber(0);
    ui->graphicsView->refreshGraphics();
  }
  updateUI();
}

void MainWindow::OnSimEnd()
{
  mIsPlaying = false;
  mWorld->mFrameManager.toLastFrame();
  ui->graphicsView->refreshGraphics();
  updateUI();
}

void MainWindow::OnSimPlayStop()
{
  if (!mWorld)
    mWorld = World::instance();

  if (mRealtimeSim)
    mWorld->mFrameManager.toLastFrame();
  mIsPlaying = !mIsPlaying;
}

void MainWindow::OnSimForward()
{
  mIsPlaying = false;
  mWorld->mFrameManager.advanceFrame();
  ui->graphicsView->refreshGraphics();
  updateUI();
}

void MainWindow::OnSimBack()
{
  mIsPlaying = false;
  mWorld->mFrameManager.rewindFrame();
  ui->graphicsView->refreshGraphics();
  updateUI();
}
